home *** CD-ROM | disk | FTP | other *** search
/ Tricks of the Mac Game Programming Gurus / TricksOfTheMacGameProgrammingGurus.iso / Book Chapters / 06 - Audio / Example Code / Hollywood.c < prev    next >
Text File  |  1995-06-16  |  45KB  |  1,831 lines

  1. //----------------
  2. // Hollywood API for use with the Macintosh Sound Manager 3.0
  3. //
  4. // This code depends upon Universal Interfaces 2.0a3 or better from Apple Computer, Inc
  5. //
  6. // History
  7. //    1/8/95    Created by Steve Hales
  8. //----------------
  9.  
  10. #include <Types.h>
  11. #include <Memory.h>
  12. #include <Quickdraw.h>
  13. #include <OSEvents.h>
  14. #include <Desk.h>
  15. #include <Events.h>
  16. #include <Resources.h>
  17. #include <Windows.h>
  18. #include <Fonts.h>
  19. #include <TextEdit.h>
  20. #include <Menus.h>
  21. #include <Dialogs.h>
  22. #include <ToolUtils.h>
  23. #include <Retrace.h>
  24. #include <StandardFile.h>
  25. #include <Sound.h>
  26. #include <SoundInput.h>
  27. #include <AIFF.h>
  28. #include <Gestalt.h>
  29. #include <Errors.h>
  30. #include <Traps.h>
  31. #include <ConditionalMacros.h>
  32. #include <FixMath.h>
  33.  
  34. #ifndef __MIXEDMODE__
  35.     #include <SysEqu.h>
  36. #else
  37.     #include <LowMem.h>
  38.     #define LMGetSoundActive() (* (unsigned char *) 0x27E)
  39. #endif
  40.  
  41. #if THINK_C
  42. #include <Think.h>
  43. #else
  44.     #define TRUE    true
  45.     #define FALSE    false
  46. #endif
  47.  
  48. #include "Hollywood.h"
  49.  
  50. #if 0
  51.     #define DEBUGSTR(x)
  52. #else
  53.     #define DEBUGSTR(x)    DebugStr(x)
  54. #endif
  55.  
  56. #define kMaxCallbackQueue    50    // total number of queued events
  57.  
  58. // Structures
  59. struct MM_SoundVoice
  60. {
  61.     SndChannelPtr            theChannel;
  62.  
  63.     CustomCallbackProc        customCallback;
  64.     short int                voiceNumber;
  65.     long                    userData;
  66.     UnsignedFixed            lastRate;
  67.  
  68.     ExtSoundHeader        theSndBuffer;
  69.     Boolean                voiceActive;
  70.     Boolean                voicePaused;
  71.     Boolean                filePlay;
  72.     FSSpec                fileSpec;
  73.     short int                fileRef;
  74. };
  75. typedef struct MM_SoundVoice    MM_SoundVoice;
  76.  
  77. // This queue is put together via an interrupt to process events at non-interrupt time
  78. struct MM_CallbackQueue
  79. {
  80.     CustomCallbackProc        customCallback;
  81.     short int                voiceNumber;
  82.     long                    userData;
  83.     short int                filePlayRef;        // 0 = no file play to close
  84.     Boolean                active;
  85. };
  86. typedef struct MM_CallbackQueue    MM_CallbackQueue;
  87.  
  88. // Variables
  89. static long                globalA5;
  90. static short int            maxSoundVoices = 0;            // Max voices allocated for Sound Manager 3.0
  91. static MM_SoundVoice    *theSoundVoices = NULL;
  92. static MM_CallbackQueue    theCallbackQueue[kMaxCallbackQueue];
  93.  
  94. static long         *    taskPtr;
  95. static VBLTask        *    theSoundVBPtr;
  96. static void            (*vblankProcPtr)(void);
  97.  
  98. static long                totalRegisteredSounds;
  99. static long                currentBlockSizeForRegisteredSounds;
  100. static SndReference    *    registeredSounds;                // current list of registered sounds
  101.  
  102. #ifdef __MIXEDMODE__
  103.     SndCallBackUPP    theMasterSoundCallbackProcPtr;
  104. #else
  105.     SndCallBackProcPtr    theMasterSoundCallbackProcPtr;
  106. #endif
  107.  
  108.  
  109. // Internal Defines
  110. #define kSoundManagerID            'sm'    // used to fix a bug in the Sound Manager. It pre-fires the callback, so
  111.                                 // you must check that its our callback. We do this by 'wasting' the param1.
  112.                                 // Note that param1 is only 16 bits.
  113.  
  114. #define kMaxSoundManagerVoices    4    // total number of voices that the Sound Manager can allocate. This may change
  115.                                 // as CPU performance increases
  116.  
  117. #define kRegisterSoundBlockSize    40    // inital number of sounds that can be registered without reallocating the
  118.                                 // block array
  119.  
  120. // forward references
  121. static pascal void HYP_HandleSoundDoneCallBack(SndChannelPtr pChannel, register SndCommand *pCmd);
  122. static void SHYP_Callback(short int voiceNumber, short int what, long userData);
  123. static OSErr HYP_PostCallbackFunction(SndChannelPtr pChannel, long userData);
  124. static OSErr HYP_AddToCallbackQueue(MM_CallbackQueue *newQueue);
  125.  
  126. // Private functions
  127. //
  128. // This will return TRUE if the new sound manager is installed, otherwise FALSE
  129.  
  130. static Boolean HYP_IsNewSoundManagerInstalled(void)
  131. {
  132.     NumVersion    theVersion;
  133.     void *        trap1;
  134.     void *        trap2;
  135.     Boolean        installed;
  136.  
  137.     installed = FALSE;
  138.     trap1 = (void *)GetToolTrapAddress(_SoundDispatch);    /* SndSoundManagerVersion Trap */
  139.     trap2 = (void *)GetToolTrapAddress(_Unimplemented);    /* Unimplemented Trap */
  140.     if (trap1 != trap2)
  141.     {
  142.         theVersion = SndSoundManagerVersion();
  143.         if (theVersion.majorRev >= 3)
  144.         {
  145.             installed = TRUE;
  146.         }
  147.     }
  148.     return installed;
  149. }
  150.  
  151.  
  152. // This will return TRUE if the virtual memory manager is installed, otherwise FALSE.
  153.  
  154. static Boolean HYP_IsVirtualMemoryAvailable(void)
  155.  {
  156.     long    feature;
  157.  
  158.     feature = 0;
  159.     if (Gestalt(gestaltVMAttr, &feature) == noErr)
  160.     {
  161.         if (feature & (1<<gestaltVMPresent))
  162.         {
  163.             return TRUE;
  164.         }
  165.     }
  166.     return FALSE;
  167. }
  168.  
  169. static short int HYP_GetFreeVoice(void)
  170. {
  171.     register short int count, freeVoice;
  172.  
  173.     freeVoice = kUseAnyVoice;
  174.     for (count = 0; count < maxSoundVoices; count++)
  175.     {
  176.         if (theSoundVoices[count].voiceActive == FALSE)
  177.         {
  178.             freeVoice = count;
  179.             break;
  180.         }
  181.     }
  182.     return freeVoice;
  183. }
  184.  
  185. static MM_SoundVoice * HYP_GetPrivateDataFromVoice(register short int voice)
  186. {
  187.     register MM_SoundVoice *pVoice;
  188.  
  189.     pVoice = NULL;
  190.     if (theSoundVoices)
  191.     {
  192.         if ( (voice < maxSoundVoices) && (voice >= 0) )
  193.         {
  194.             if (theSoundVoices[voice].theChannel)
  195.             {
  196.                 pVoice = &theSoundVoices[voice];
  197.             }
  198.         }
  199.     }
  200.     return pVoice;
  201. }
  202.  
  203.  
  204. static short int HYP_GetVoiceFromChannel(register SndChannelPtr pChannel)
  205. {
  206.     register short int count;
  207.  
  208.     count = -1;
  209.     for (count = 0; count < maxSoundVoices; count++)
  210.     {
  211.         if (pChannel == theSoundVoices[count].theChannel)
  212.         {
  213.             break;
  214.         }
  215.     }
  216.     return count;
  217. }
  218.  
  219. // For use with Apple's Sound Manager
  220. static pascal void HYP_FilePlayCompletionDone(SndChannelPtr pChannel)
  221. {
  222.     register long                    saveA5;
  223.     register MM_SoundVoice            *pVoice;
  224.     register short int                voiceNumber;
  225.     MM_CallbackQueue                newQueue;
  226.  
  227. #if GENERATING68K == 1
  228.     saveA5 = SetA5(pChannel->userInfo);         /* restore previous a5 */
  229. #else
  230.     saveA5;
  231. #endif
  232.     voiceNumber = HYP_GetVoiceFromChannel(pChannel);
  233.     pVoice = HYP_GetPrivateDataFromVoice(voiceNumber);
  234.     if (pVoice)
  235.     {
  236.         if (pChannel != pVoice->theChannel)
  237.         {
  238.             DEBUGSTR("\pChannel doesn't match voiceNumber");
  239.         }
  240.  
  241.         if (pVoice->customCallback)
  242.         {
  243.             (*pVoice->customCallback)(voiceNumber, 
  244.                                     kSoundDoneNormalStop, 
  245.                                     pVoice->userData);
  246.             newQueue.customCallback = pVoice->customCallback;
  247.             newQueue.voiceNumber = pVoice->voiceNumber;
  248.             newQueue.userData = pVoice->userData;
  249.             newQueue.filePlayRef = pVoice->fileRef;
  250.             if (HYP_AddToCallbackQueue(&newQueue))
  251.             {
  252.                 DEBUGSTR("\pQueue Full!");
  253.             }
  254.         }
  255.         pVoice->voiceActive = FALSE;
  256.     }
  257.     /* Restore A5 for the rest of the interupt process
  258.     */
  259. #if GENERATING68K == 1
  260.     SetA5(saveA5);         /* restore previous a5 */
  261. #endif
  262.  
  263. }
  264.  
  265.  
  266. static pascal void HYP_HandleSoundDoneCallBack(SndChannelPtr pChannel, register SndCommand *pCmd)
  267. {
  268.     register long                    saveA5;
  269.     register MM_SoundVoice            *pVoice;
  270.     register short int                voiceNumber;
  271.     MM_CallbackQueue                newQueue;
  272.  
  273.     if (pCmd->param1 == kSoundManagerID)
  274.     {
  275. #if GENERATING68K == 1
  276.         saveA5 = SetA5(pChannel->userInfo);         /* restore previous a5 */
  277. #else
  278.         saveA5;
  279. #endif
  280.         voiceNumber = HYP_GetVoiceFromChannel(pChannel);
  281.         pVoice = HYP_GetPrivateDataFromVoice(voiceNumber);
  282.         if (pVoice)
  283.         {
  284.             if (pChannel != pVoice->theChannel)
  285.             {
  286.                 DEBUGSTR("\pChannel doesn't match voiceNumber");
  287.             }
  288.  
  289.             if (pVoice->customCallback)
  290.             {
  291.                 (*pVoice->customCallback)(voiceNumber, 
  292.                                         kSoundDoneNormalStop, 
  293.                                         pVoice->userData);
  294.                 newQueue.customCallback = pVoice->customCallback;
  295.                 newQueue.voiceNumber = pVoice->voiceNumber;
  296.                 newQueue.userData = pVoice->userData;
  297.                 newQueue.filePlayRef = 0;
  298.                 if (HYP_AddToCallbackQueue(&newQueue))
  299.                 {
  300.                     DEBUGSTR("\pQueue Full!");
  301.                 }
  302.             }
  303.             pVoice->voiceActive = FALSE;
  304.         }
  305.         /* Restore A5 for the rest of the interupt process
  306.         */
  307. #if GENERATING68K == 1
  308.         SetA5(saveA5);         /* restore previous a5 */
  309. #endif
  310.     }
  311. }
  312.  
  313. static OSErr HYP_PostCallbackFunction(SndChannelPtr pChannel, long userData)
  314. {
  315.     SndCommand    theCmd;
  316.     OSErr        theErr;
  317.  
  318.     if (pChannel)
  319.     {
  320.         theCmd.cmd = callBackCmd;
  321.         theCmd.param1 = kSoundManagerID;                            // used for ID. Bug in SM that a callback is
  322.                                                             // sometimes called at the begining of a sample
  323.                                                             // with the wrong information.
  324.                                                             // Use this to make sure that it is our callback.
  325.         theCmd.param2 = userData;
  326.         pChannel->userInfo = globalA5;
  327.         theErr = SndDoCommand(pChannel, &theCmd, FALSE);
  328.     }
  329.     else
  330.     {
  331.         theErr = badChannel;
  332.     }
  333.     return theErr;
  334. }
  335.  
  336.  
  337. static void HYP_SetupQueues(void)
  338. {
  339.     short int count;
  340.  
  341.     for (count = 0; count < kMaxCallbackQueue; count++)
  342.     {
  343.         theCallbackQueue[count].active = FALSE;
  344.         theCallbackQueue[count].customCallback = NULL;
  345.     }
  346. }
  347.  
  348. static void HYP_CleanupQueues(void)
  349. {
  350. }
  351.  
  352. static OSErr HYP_AddToCallbackQueue(MM_CallbackQueue *newQueue)
  353. {
  354.     OSErr    theErr;
  355.     short int    count;
  356.     Boolean    foundQueue;
  357.  
  358. //    DEBUGSTR("\pHYP_AddToCallbackQueue");
  359.     theErr = noErr;
  360.  
  361.     foundQueue = FALSE;
  362.     for (count = 0; count < kMaxCallbackQueue; count++)
  363.     {
  364.         if (theCallbackQueue[count].active == FALSE)
  365.         {
  366.             theCallbackQueue[count] = *newQueue;
  367.             theCallbackQueue[count].active = TRUE;
  368.             foundQueue = TRUE;
  369.             break;
  370.         }
  371.     }
  372.     if (foundQueue == FALSE)
  373.     {
  374.         theErr = qErr;
  375.     }
  376.     return theErr;
  377. }
  378.  
  379. static void HYP_ProcessNextCallbackQueue(void)
  380. {
  381.     register MM_CallbackQueue *    theQueue;
  382.     short int                    count;
  383.  
  384. //    DEBUGSTR("\pHYP_ProcessNextCallbackQueue");
  385.     for (count = 0; count < kMaxCallbackQueue; count++)
  386.     {
  387.         theQueue = &theCallbackQueue[count];
  388.         if (theQueue->active)
  389.         {
  390. //            DEBUGSTR("\pHYP_ProcessNextCallbackQueue:BEFORE CALLBACK");
  391.             if (theQueue->filePlayRef)
  392.             {
  393.                 FSClose(theQueue->filePlayRef);
  394.                 theQueue->filePlayRef = 0;
  395.             }
  396.             if (theQueue->customCallback)
  397.             {
  398.                 (*theQueue->customCallback)(theQueue->voiceNumber, kSoundDoneNoInterrupt, theQueue->userData);
  399.                 theQueue->customCallback = NULL;
  400.             }
  401.             theQueue->active = FALSE;
  402.         }
  403.     }
  404. }
  405.  
  406. // General purpose VBL task to execute user defined task
  407. static void HYP_ProcessVBLTask(void)
  408. {
  409.     if (vblankProcPtr)
  410.     {
  411.         (*vblankProcPtr)();
  412.     }
  413. }
  414.  
  415. #if GENERATING68K == 1
  416. // 68k based VBL task function
  417. #pragma parameter __D0 GetA0toVariable
  418. pascal long GetA0toVariable(void) = {0x2028, 0xFFFC}; 
  419.  
  420. static pascal void HYP_PreProcessVBLTask(void)
  421. {
  422.     long            saveA5, newA5;
  423.  
  424.     newA5 = GetA0toVariable();    /* Globals register points the vbl task structure */
  425.  
  426.     saveA5 = SetA5(newA5);            /* set current a5 */
  427.     theSoundVBPtr->vblCount = 1;        /* tell it to contiue */
  428.  
  429.     HYP_ProcessVBLTask();
  430.  
  431.     /* Restore A5 for the rest of the interupt process
  432.     */
  433.     SetA5(saveA5);         /* restore previous a5 */
  434. }
  435. #else
  436. // PowerPC based VBL task function
  437. static pascal void HYP_PreProcessVBLTask(VBLTaskPtr theVBLTask) 
  438. {
  439.     theSoundVBPtr->vblCount = 1;        /* tell it to contiue */
  440.  
  441.     HYP_ProcessVBLTask();
  442. }
  443.  
  444. #endif
  445.  
  446.  
  447. #if GENERATING68K == 1
  448. // 68k based VBL task setup and cleanup
  449. static OSErr HYP_SetupVBLTask(void)
  450. {
  451.     OSErr theErr;
  452.     struct Jump
  453.     {
  454.         short     bsr_inderect;        // 0
  455.         void *    address;            // 4
  456.         long        movea;            // 8
  457.         short    jmp_a1;            // 12
  458.     };
  459.     struct Jump *heapJumpPtr;
  460.  
  461. /*
  462.         0x6104,             bsr.s 6(pc)
  463.         0x0000, 0x0000,     dc.l    0
  464.         0x225F             movea.l (a7)+, a1
  465.         0x2251             move (a1), a1
  466.         0x4Ed1             jmp (a1)
  467. */
  468.  
  469. /* Set up Vertical Blank Interrupt
  470. */
  471.     taskPtr = (long *)NewPtrClear((long)sizeof(VBLTask) + sizeof(long));
  472.     heapJumpPtr = (struct Jump *)NewPtrSys((long)sizeof(struct Jump));
  473.     if ( (taskPtr) && (heapJumpPtr) )
  474.     {
  475.         if (HYP_IsVirtualMemoryAvailable())
  476.         {
  477.             LockMemory(taskPtr, (long)sizeof(VBLTask) + sizeof(long));
  478.             LockMemory(heapJumpPtr, (long)sizeof(struct Jump));
  479.         }
  480.  
  481.         theSoundVBPtr = (VBLTask *)( ((Byte *)taskPtr) + sizeof(long) );
  482.         taskPtr[0] = SetCurrentA5();
  483.         heapJumpPtr->bsr_inderect = 0x6104;
  484.         heapJumpPtr->address = HYP_PreProcessVBLTask;
  485.         heapJumpPtr->movea = 0x225F2251L;
  486.         heapJumpPtr->jmp_a1 = 0x4Ed1;
  487.  
  488.         theSoundVBPtr->vblAddr = (VBLUPP)heapJumpPtr;
  489.         theSoundVBPtr->vblCount = 1;        /* Every 1/60th of a second */
  490.         theSoundVBPtr->qType = vType;
  491.         theSoundVBPtr->qLink = NULL;
  492.         theSoundVBPtr->vblPhase = 0;
  493.  
  494. /* Ok, flush the code/data cache for the '040 Macs. */
  495. #ifndef __MIXEDMODE__
  496.         if (*((char *)CPUFlag) >= 2)
  497. #else
  498.         if (LMGetCPUFlag() >= 2)
  499. #endif
  500.         {
  501.             FlushInstructionCache();
  502.             FlushDataCache();
  503.         }
  504.  
  505.         theErr = VInstall((QElemPtr)theSoundVBPtr);
  506.     }
  507.     else
  508.     {
  509.         if (heapJumpPtr)
  510.         {
  511.             if (HYP_IsVirtualMemoryAvailable())
  512.             {
  513.                 UnlockMemory((Ptr)heapJumpPtr, 12L);
  514.             }
  515.             DisposePtr((Ptr)heapJumpPtr);
  516.         }
  517.         if (taskPtr)
  518.         {
  519.             if (HYP_IsVirtualMemoryAvailable())
  520.             {
  521.                 UnlockMemory(taskPtr, (long)sizeof(VBLTask) + sizeof(long));
  522.             }
  523.             DisposePtr((Ptr)taskPtr);
  524.             taskPtr = NULL;
  525.             theSoundVBPtr = NULL;
  526.         }
  527.     }
  528.     return theErr;
  529. }
  530.  
  531. static OSErr HYP_CleanupVBLTask(void)
  532. {
  533.     if (theSoundVBPtr)
  534.     {
  535.         if (theSoundVBPtr->vblAddr)
  536.         {
  537.             if (HYP_IsVirtualMemoryAvailable())
  538.             {
  539.                 UnlockMemory((Ptr)theSoundVBPtr->vblAddr, 12L);
  540.             }
  541.             DisposePtr((Ptr)theSoundVBPtr->vblAddr);
  542.         }
  543.         VRemove((QElemPtr)theSoundVBPtr);
  544.         theSoundVBPtr = NULL;
  545.     }
  546.     if (taskPtr)
  547.     {
  548.         if (HYP_IsVirtualMemoryAvailable())
  549.         {
  550.             UnlockMemory(taskPtr, (long)sizeof(VBLTask) + sizeof(long));
  551.         }
  552.         DisposePtr((Ptr)taskPtr);
  553.     }
  554.     return noErr;
  555. }
  556. #else
  557. // PowerPC based VBL task setup and cleanup
  558. static OSErr HYP_SetupVBLTask(void)
  559. {
  560.     OSErr    theErr;
  561. /* Set up Vertical Blank Interrupt
  562. */
  563.     theErr = noErr;
  564.     taskPtr = NULL;
  565.     theSoundVBPtr = (VBLTask *)NewPtrSysClear((long)sizeof(VBLTask));
  566.     if (theSoundVBPtr)
  567.     {
  568.         if (HYP_IsVirtualMemoryAvailable())
  569.         {
  570.             LockMemory(theSoundVBPtr, (long)sizeof(VBLTask));
  571.         }
  572.         theSoundVBPtr->vblAddr = NewVBLProc(HYP_PreProcessVBLTask);
  573.         theSoundVBPtr->vblCount = 1;        /* Every 1/60th of a second */
  574.         theSoundVBPtr->qType = vType;
  575.         theSoundVBPtr->qLink = NULL;
  576.         theSoundVBPtr->vblPhase = 0;
  577.  
  578.         theErr = VInstall((QElemPtr)theSoundVBPtr);
  579.     }
  580.     return theErr;
  581. }
  582.  
  583. static OSErr HYP_CleanupVBLTask(void)
  584. {
  585.     if (theSoundVBPtr)
  586.     {
  587.         if (theSoundVBPtr->vblAddr)
  588.         {
  589.             DisposeRoutineDescriptor((VBLUPP)theSoundVBPtr->vblAddr);
  590.             theSoundVBPtr->vblAddr = NULL;
  591.         }
  592.         VRemove((QElemPtr)theSoundVBPtr);
  593.  
  594.         if (HYP_IsVirtualMemoryAvailable())
  595.         {
  596.             UnlockMemory(theSoundVBPtr, (long)sizeof(VBLTask));
  597.         }
  598.         DisposePtr((Ptr)theSoundVBPtr);
  599.         theSoundVBPtr = NULL;
  600.     }
  601.     return noErr;
  602. }
  603. #endif
  604.  
  605. static Boolean HYP_IsThisSoundRegistered(SndReference theSound)
  606. {
  607.     register short int    count;
  608.     register Boolean    foundPlace;
  609.  
  610.     foundPlace = FALSE;
  611.     for (count = 0; count < totalRegisteredSounds; count++)
  612.     {
  613.         if (registeredSounds[count] == theSound)
  614.         {
  615.             foundPlace = TRUE;
  616.         }
  617.     }
  618.     return foundPlace;
  619. }
  620.  
  621.  
  622. static void HYP_RegisterThisSound(SndReference theSound)
  623. {
  624.     register short int    count;
  625.     register Boolean    foundPlace;
  626.     SndReference *        newArray;
  627.     register long        theSize;
  628.  
  629.     if (registeredSounds == NULL)        // first time to register?
  630.     {
  631.         theSize = (long)sizeof(SndReference *) * kRegisterSoundBlockSize;
  632.         registeredSounds = (SndReference *)NewPtrClear(theSize);
  633.         totalRegisteredSounds = 0;
  634.         currentBlockSizeForRegisteredSounds = kRegisterSoundBlockSize;
  635.     }
  636.     if (registeredSounds)
  637.     {
  638.         if (HYP_IsThisSoundRegistered(theSound) == FALSE)        // don't register a sound twice
  639.         {
  640.             // walk through the sound register array and see if there are any free blocks, if so, then put
  641.             // our reference there
  642.             foundPlace = FALSE;
  643.             for (count = 0; count < totalRegisteredSounds; count++)
  644.             {
  645.                 if (registeredSounds[count] == NULL)
  646.                 {
  647.                     registeredSounds[count] = theSound;
  648.                     foundPlace = TRUE;
  649.                 }
  650.             }
  651.             // if we didn't find a place in our array, go ahead and put it into the next place. if we've need more
  652.             // in the array then reallocate and then place it in there
  653.             if (foundPlace == FALSE)
  654.             {
  655.                 totalRegisteredSounds++;        // next entry
  656.                 if (totalRegisteredSounds > currentBlockSizeForRegisteredSounds)
  657.                 {
  658.                     // out of space, so reallocate a new array and copy all the current registered sounds into it
  659.                     theSize = sizeof(SndReference *) * (currentBlockSizeForRegisteredSounds + kRegisterSoundBlockSize);
  660.                     newArray = (SndReference *)NewPtrClear(theSize);
  661.                     if (newArray)
  662.                     {
  663.                         theSize = (long)sizeof(SndReference *) * currentBlockSizeForRegisteredSounds;
  664.                         BlockMove((Ptr)registeredSounds, (Ptr)newArray, theSize);
  665.                         DisposePtr((Ptr)registeredSounds);
  666.  
  667.                         registeredSounds = newArray;        // replace old one with new one
  668.                         currentBlockSizeForRegisteredSounds += kRegisterSoundBlockSize;    // increase our tollerance
  669.                     }
  670.                 }
  671.                 // we have enough room in our current array or the array has been enlarged,
  672.                 // so put our reference in the next place
  673.                 registeredSounds[totalRegisteredSounds-1] = theSound;
  674.             }
  675.         }
  676.     }
  677. }
  678.  
  679. static void HYP_UnregisterThisSound(SndReference theSound)
  680. {
  681.     register short int    count;
  682.  
  683.     // walk through our reference array and remove by setting the placeholder to NULL, our
  684.     // reference. We never shrink the reference array because it will never get that big, and
  685.     // its wastes time
  686.     if (registeredSounds)
  687.     {
  688.         for (count = 0; count < totalRegisteredSounds; count++)
  689.         {
  690.             if (registeredSounds[count] == theSound)
  691.             {
  692.                 registeredSounds[count] = NULL;        // ok remove from our list
  693.             }
  694.         }
  695.     }
  696. }
  697.  
  698. static void HYP_UnregisterAllSounds(void)
  699. {
  700.     register short int    count;
  701.  
  702.     // walk through our reference array and remove by setting the placeholder to NULL, our
  703.     // reference. At this point dispose of the reference array too
  704.     if (registeredSounds)
  705.     {
  706.         for (count = 0; count < totalRegisteredSounds; count++)
  707.         {
  708.             if (registeredSounds[count])
  709.             {
  710.                 registeredSounds[count] = NULL;        // ok remove from our list
  711.             }
  712.         }
  713.         DisposePtr((Ptr)registeredSounds);
  714.         registeredSounds = NULL;
  715.     }
  716. }
  717.  
  718. // Public functions
  719. //
  720.  
  721. void HY_SetSoundVBCallBack(void (*theProc)(void))
  722. {
  723.     vblankProcPtr = theProc;
  724. }
  725.  
  726.  
  727. #define kCompressionPacketSize    6    // 2 bytes at 3:1 is 6 bytes for a packet
  728.                                 // 1 byte at 6:1 is 6 bytes also
  729. Handle HY_GetMACESound(register CmpSoundHeaderPtr pSndBuffer, 
  730.                     Ptr *pWave, long *length, 
  731.                     long *pLoopStart, long *loopend, 
  732.                     long *rate)
  733. {
  734.     Ptr                inBuffer, outBuffer, theInState, theOutState;
  735.     CmpSoundHeader *    csndHeaderPtr;
  736.     Handle            outHandle;
  737.     unsigned long        sampleCount;
  738.     long                buffLen;
  739.  
  740.     outHandle = NULL;
  741.     csndHeaderPtr = (CmpSoundHeaderPtr) pSndBuffer;
  742.     if (csndHeaderPtr->compressionID)                     // see if sound is compressed
  743.     {
  744.         sampleCount = csndHeaderPtr->numFrames;        // get number of number of frames
  745.         buffLen = sampleCount * kCompressionPacketSize;    // bufferLen = number of frames * packet size */
  746.  
  747.         theInState = NewPtrClear(128L);                // allocate working buffers
  748.         theOutState = NewPtrClear(128L);
  749.         outHandle = NewHandleClear(buffLen);
  750.         if ((theInState && theOutState && outHandle))
  751.         {
  752.             HLock(outHandle);
  753.             outBuffer = *outHandle;
  754.             if ((inBuffer = csndHeaderPtr->samplePtr) == NULL)    // get ptr to sample data
  755.             {
  756.                 inBuffer = (Ptr) csndHeaderPtr->sampleArea;
  757.             }                                                /* decompress sound */
  758.  
  759.             switch(csndHeaderPtr->compressionID)    // MACE compression ID's only
  760.             {
  761.                 case threeToOne:
  762.                     Exp1to3(inBuffer, outBuffer, sampleCount, (StateBlockPtr)theInState, (StateBlockPtr)theOutState,
  763.                                     csndHeaderPtr->numChannels, 1);
  764.                     break;
  765.                 case sixToOne:
  766.                     Exp1to6(inBuffer,outBuffer, sampleCount, (StateBlockPtr)theInState, (StateBlockPtr)theOutState,
  767.                                     csndHeaderPtr->numChannels, 1);
  768.                     break;
  769.                 default:
  770.                     BlockMove(inBuffer, outBuffer, sampleCount);
  771.                     break;
  772.             }
  773.             *pWave = outBuffer;
  774.             *length = buffLen;
  775.             *pLoopStart = csndHeaderPtr->loopStart;
  776.             *loopend = csndHeaderPtr->loopEnd;
  777.             *rate = csndHeaderPtr->sampleRate;
  778.         }
  779.         if (theInState)
  780.         {
  781.             DisposePtr(theInState);
  782.         }
  783.         if (theOutState)
  784.         {
  785.             DisposePtr(theOutState);
  786.         }
  787.     }
  788.     return outHandle;
  789. }
  790.  
  791.  
  792. void HY_RegisterThisSound(SndReference theSound)
  793. {
  794.     if (theSound)
  795.     {
  796.         HLock(theSound);
  797.         HYP_RegisterThisSound(theSound);            // register this sound
  798.     }
  799. }
  800.  
  801.  
  802. SndReference HY_GetSoundResource(short int resourceID)
  803. {
  804.     Handle                theSoundData;
  805.     SndReference            theSndRef;
  806.  
  807.     theSndRef = NULL;
  808.     theSoundData = GetResource('snd ', resourceID);        // get the resource data type 'snd '
  809.     if (theSoundData)
  810.     {
  811.         HLock(theSoundData);                        // lock it down
  812.         HYP_RegisterThisSound(theSoundData);            // register this sound
  813.         theSndRef = (SndReference)theSoundData;
  814.     }
  815.     return theSndRef;
  816. }
  817.  
  818. void HY_UnregisterSoundResource(SndReference theSound)
  819. {
  820.     if (HYP_IsThisSoundRegistered(theSound))        // only work with sounds that have been access through our API
  821.     {
  822.         HYP_UnregisterThisSound(theSound);
  823.         HUnlock((Handle)theSound);
  824.     }
  825. }
  826.  
  827. void HY_UnregisterAllSoundResources(void)
  828. {
  829.     register short int    count;
  830.  
  831.     // walk through our reference array and remove by setting the placeholder to NULL, our
  832.     // reference. At this point dispose of the reference array too
  833.     if (registeredSounds)
  834.     {
  835.         for (count = 0; count < totalRegisteredSounds; count++)
  836.         {
  837.             if (registeredSounds[count])
  838.             {
  839.                 HY_UnregisterSoundResource(registeredSounds[count]);        // ok remove from our list
  840.             }
  841.         }
  842.         HYP_UnregisterAllSounds();
  843.     }
  844. }
  845.  
  846.  
  847. Boolean HY_Is16BitAvailable(void)
  848. {
  849.     long    feature;
  850.  
  851.     feature = 0;
  852.     if (Gestalt(gestaltSoundAttr, &feature) == noErr)
  853.     {
  854.         if (feature & (1<<gestalt16BitSoundIO))
  855.         {
  856.             return TRUE;
  857.         }
  858.     }
  859.     return FALSE;
  860. }
  861.  
  862. Boolean HY_IsStereoAvailable(void)
  863. {
  864.     long    feature;
  865.  
  866.     feature = 0;
  867.     if (Gestalt(gestaltSoundAttr, &feature) == noErr)
  868.     {
  869.         if (feature & (1<<gestaltStereoCapability))
  870.         {
  871.             return TRUE;
  872.         }
  873.     }
  874.     return FALSE;
  875. }
  876.  
  877.  
  878. OSErr HY_Setup(short int featureMask, short int maxVoices)
  879. {
  880.     OSErr                theErr;
  881.     short int                count;
  882.     register MM_SoundVoice    *pVoice;
  883.     register long            features;
  884.     SoundVolume            fullVolume;
  885.  
  886.     if (HYP_IsNewSoundManagerInstalled())
  887.     {
  888.         theErr = noErr;
  889.         maxSoundVoices = 0;
  890.         fullVolume.left = kFullVolume;
  891.         fullVolume.right = kFullVolume;
  892.  
  893.         registeredSounds = NULL;        // no sounds registered
  894.  
  895. #if GENERATING68K == 1
  896.         globalA5 = (long)SetCurrentA5();        // we do this here, so HY_PlaySample can be called via interrupt
  897. #else
  898.         globalA5 = 0;
  899. #endif
  900.         theSoundVoices = (MM_SoundVoice *)NewPtrClear((long)sizeof(MM_SoundVoice) * maxVoices);
  901.         if (theSoundVoices)
  902.         {
  903. #ifdef __MIXEDMODE__
  904.             theMasterSoundCallbackProcPtr = NewSndCallBackProc(HYP_HandleSoundDoneCallBack);
  905. #else
  906.             theMasterSoundCallbackProcPtr = (void *)HYP_HandleSoundDoneCallBack;
  907. #endif
  908.  
  909.             features = 0L;
  910.             if ((featureMask & kUseStereo) == kUseStereo)
  911.             {
  912.                 features |= initStereo;        // stereo sound support
  913.             }
  914.             else
  915.             {
  916.                 features |= initMono;            // mono sound support
  917.             }
  918.             if ((featureMask & kMaxQuality) == kMaxQuality)
  919.             {
  920.                 features |= initNoDrop;        // keep interpolation on, turn off drop sample convertion (CPU heavy)
  921.             }
  922.             else
  923.             {
  924.                 features |= initNoInterp;        // turn off interpolation
  925.             }
  926.             for (count = 0; count < maxVoices; count++)
  927.             {
  928.                 pVoice = &theSoundVoices[count];
  929.                 theErr = SndNewChannel(&pVoice->theChannel, 
  930.                             sampledSynth,
  931.                             features,
  932.                             theMasterSoundCallbackProcPtr);
  933.                 if (theErr)
  934.                 {    /* we failed, so bail
  935.                     */
  936.                     pVoice->theChannel = NULL;
  937.                     HY_Cleanup();
  938.                     break;
  939.                 }
  940.                 else
  941.                 {    // success
  942.                     maxSoundVoices++;        // we increment our global in case we fail so we can back
  943.                                         // out easily
  944.                     pVoice->voiceActive = FALSE;
  945.                     pVoice->voicePaused = FALSE;
  946.                     pVoice->filePlay = FALSE;
  947.                     pVoice->theChannel->callBack = theMasterSoundCallbackProcPtr;    // extra, just in case
  948.                     pVoice->theChannel->userInfo = globalA5;
  949.                 }
  950.             }
  951.             for (count = 0; count < maxVoices; count++)
  952.             {
  953.                 HY_SetVolume(count, fullVolume);
  954.                 if (HY_Is16BitAvailable())
  955.                 {
  956.                     HY_SetRate(count, rate22050hz);
  957.                 }
  958.                 else
  959.                 {
  960.                     HY_SetRate(count, rate22khz);
  961.                 }
  962.             }
  963.             HYP_SetupQueues();
  964.             HYP_SetupVBLTask();
  965.         }
  966.         else
  967.         {
  968.             theErr = memFullErr;
  969.         }
  970.     }
  971.     else
  972.     {
  973.         theErr = smRevisionErr;    // we can only work with Sound Manager 3.0. We're using cool features of SM 3.0
  974.     }
  975.     return theErr;
  976. }
  977.  
  978. OSErr HY_Cleanup(void)
  979. {
  980.     register short int    count;
  981.  
  982.     HYP_CleanupVBLTask();            // clean up our VBL task
  983.     HY_UnregisterAllSoundResources();    // clean up and remove all references to sounds. This calls HYP_UnregisterAllSounds
  984.  
  985.     if (theSoundVoices)
  986.     {
  987.         for (count = 0; count < maxSoundVoices; count++)
  988.         {
  989.             if (theSoundVoices[count].theChannel)
  990.             {
  991.                 HY_StopSample(count);
  992.     
  993.                 SndDisposeChannel(theSoundVoices[count].theChannel, TRUE);
  994.                 theSoundVoices[count].theChannel = NULL;
  995.             }
  996.         }
  997. #ifdef __MIXEDMODE__
  998.         if (theMasterSoundCallbackProcPtr)
  999.         {
  1000.             DisposeRoutineDescriptor(theMasterSoundCallbackProcPtr);
  1001.         }
  1002. #endif
  1003.  
  1004.         theMasterSoundCallbackProcPtr = NULL;
  1005.         DisposePtr((Ptr)theSoundVoices);
  1006.         theSoundVoices = NULL;
  1007.     }
  1008.     return noErr;
  1009. }
  1010.  
  1011. Boolean HY_Active(void)
  1012. {
  1013.     return (theSoundVoices) ? TRUE : FALSE;
  1014. }
  1015.  
  1016. OSErr HY_PlaySample(    short int voiceNumber,                     // voice to play sample on
  1017.                     void *pSample,                         // morph data pointer
  1018.                     long length,                             // sample length
  1019.                     UnsignedFixed rate,                                // Fixed 16.16 value
  1020.                     short dataBitSize,                        // 8 or 16 bit data
  1021.                     short channelSize,                        // 1 or 2 channels of date
  1022.                     CustomCallbackProc    customCallback,        // callback when finished, event what
  1023.                     long userData,
  1024.                     Boolean killSound)
  1025. {
  1026.     SndCommand            theCmd;
  1027.     register MM_SoundVoice    *pVoice;
  1028.     register OSErr            theErr;
  1029.     ExtSoundHeader        theSndBuffer;
  1030.  
  1031.     theErr = noErr;
  1032.     if (voiceNumber == kUseAnyVoice)
  1033.     {
  1034.         voiceNumber = HYP_GetFreeVoice();
  1035.         if (voiceNumber == kUseAnyVoice)
  1036.         {
  1037.             theErr = channelBusy;
  1038.         }
  1039.     }
  1040.     if (length < 1)                            // data length greater than zero
  1041.     {
  1042.         theErr = buffersTooSmall;
  1043.     }
  1044.     if ( (dataBitSize != 8) && (dataBitSize != 16) )    // sample bit size is 8 or 16 bits
  1045.     {
  1046.         theErr = badFormat;
  1047.     }
  1048.     if ( (channelSize != 1) && (channelSize != 2) )    // mono or stereo
  1049.     {
  1050.         theErr = badFormat;
  1051.     }
  1052.     if (rate < 0x10000L)                        // sample rate is at least 1.0
  1053.     {
  1054.         theErr = siInvalidSampleRate;
  1055.     }
  1056.     if (theErr == noErr)
  1057.     {
  1058.         pVoice = HYP_GetPrivateDataFromVoice(voiceNumber);        // voice to play on is in range
  1059.         if (pVoice)
  1060.         {
  1061.             if (pVoice->voiceActive)
  1062.             {
  1063.                 if (killSound)
  1064.                 {
  1065.                     HY_StopSample(voiceNumber);
  1066.                 }
  1067.                 else
  1068.                 {
  1069.                     theErr = channelBusy;
  1070.                 }
  1071.             }
  1072.             if (theErr == noErr)
  1073.             {
  1074.                 pVoice->customCallback = customCallback;
  1075.                 pVoice->userData = userData;
  1076.                 /* Play sample */
  1077.     
  1078.                 theSndBuffer.samplePtr = (Ptr)pSample;
  1079.                 theSndBuffer.numChannels = channelSize;
  1080.                 theSndBuffer.sampleRate =  rate;
  1081.                 theSndBuffer.loopStart = 0;        // Apple Sound Manager looping doesn't work for one shot sounds
  1082.                 theSndBuffer.loopEnd = 0;
  1083.                 theSndBuffer.encode = extSH;
  1084.                 theSndBuffer.baseFrequency = 0;
  1085.                 theSndBuffer.numFrames = length;
  1086. //                theSndBuffer.AIFFSampleRate = 0;
  1087.                 theSndBuffer.markerChunk = NULL;
  1088.                 theSndBuffer.instrumentChunks = NULL;
  1089.                 theSndBuffer.AESRecording = NULL;
  1090.                 theSndBuffer.sampleSize = dataBitSize;
  1091.                 theSndBuffer.futureUse1 = 0;
  1092.                 theSndBuffer.futureUse2 = 0;
  1093.                 theSndBuffer.futureUse3 = 0;
  1094.                 theSndBuffer.futureUse4 = 0;
  1095.                 pVoice->theSndBuffer = theSndBuffer;
  1096.                 theCmd.param1 = 0;
  1097.                 theCmd.param2 = (long)&theSndBuffer;
  1098.                 theCmd.cmd = bufferCmd;
  1099.                 theErr = SndDoCommand(pVoice->theChannel, &theCmd, FALSE);        // start sound
  1100.                 if (theErr == noErr)
  1101.                 {
  1102.                     theErr = HYP_PostCallbackFunction(pVoice->theChannel, (long)pVoice);
  1103.                 }
  1104.                 pVoice->voiceActive = TRUE;
  1105.             }
  1106.         }
  1107.         else
  1108.         {
  1109.             theErr = qtParamErr;
  1110.         }
  1111.     }
  1112.     if (theErr)
  1113.     {
  1114.         DEBUGSTR("\pError in HY_PlaySample");
  1115.     }
  1116.     return theErr;
  1117. }
  1118.  
  1119. void HY_StopSample(register short int voiceNumber)
  1120. {
  1121.     register MM_SoundVoice    *pVoice;
  1122.     SndCommand            theCmd;
  1123.  
  1124.     if (voiceNumber == kUseAnyVoice)
  1125.     {
  1126.         voiceNumber = HYP_GetFreeVoice();
  1127.     }
  1128.  
  1129.     pVoice = HYP_GetPrivateDataFromVoice(voiceNumber);
  1130.     if (pVoice)
  1131.     {
  1132.         if (HY_IsVoiceEmpty(voiceNumber) == FALSE)
  1133.         {
  1134.             if (pVoice->filePlay)
  1135.             {
  1136.                 SndStopFilePlay(pVoice->theChannel, TRUE);
  1137.                 FSClose(pVoice->fileRef);
  1138.                 pVoice->filePlay = FALSE;
  1139.                 pVoice->fileRef = 0;
  1140.             }
  1141.             theCmd.param1 = 0;
  1142.             theCmd.param2 = 0;
  1143.             theCmd.cmd = quietCmd;
  1144.             SndDoImmediate(pVoice->theChannel, &theCmd);
  1145.             theCmd.cmd = flushCmd;
  1146.             SndDoImmediate(pVoice->theChannel, &theCmd);
  1147.             if (pVoice->customCallback)
  1148.             {
  1149.                 (*pVoice->customCallback)(    voiceNumber, 
  1150.                                         kSoundDoneForcedStop, 
  1151.                                         pVoice->userData);
  1152.             }
  1153.             pVoice->voiceActive = FALSE;
  1154.         }
  1155.     }
  1156. }
  1157.  
  1158. OSErr HY_PlaySoundHandle(    short int voiceNumber, 
  1159.                         SndReference theSound,
  1160.                         CustomCallbackProc customCallback,    
  1161.                         long userData,
  1162.                         Boolean killSound)
  1163. {
  1164.     register MM_SoundVoice    *pVoice;
  1165.     register OSErr            theErr;
  1166.  
  1167.     theErr = noErr;
  1168.     if (voiceNumber == kUseAnyVoice)
  1169.     {
  1170.         voiceNumber = HYP_GetFreeVoice();
  1171.         if (voiceNumber == kUseAnyVoice)
  1172.         {
  1173.             theErr = channelBusy;
  1174.         }
  1175.     }
  1176.     // only work with sounds that have been access through our API
  1177.     if ( (theErr == noErr) && (HYP_IsThisSoundRegistered(theSound)))
  1178.     {
  1179.         pVoice = HYP_GetPrivateDataFromVoice(voiceNumber);        // voice to play on is in range
  1180.         if (pVoice)
  1181.         {
  1182.             if (pVoice->voiceActive)
  1183.             {
  1184.                 if (killSound)
  1185.                 {
  1186.                     HY_StopSample(voiceNumber);
  1187.                 }
  1188.                 else
  1189.                 {
  1190.                     theErr = channelBusy;
  1191.                 }
  1192.             }
  1193.             if (theErr == noErr)
  1194.             {
  1195.                 pVoice->customCallback = customCallback;
  1196.                 pVoice->userData = userData;
  1197.  
  1198.                 // Play sound sample. This allows the sound reference to be a MACE compressed sound, 8 or 16 bit, mono,
  1199.                 // stereo, or whatever.
  1200.                 theErr = SndPlay(pVoice->theChannel, (SndListHandle)theSound, TRUE);
  1201.                 if (theErr == noErr)
  1202.                 {
  1203.                     // now post a callback to process this sample when its finished
  1204.                     theErr = HYP_PostCallbackFunction(pVoice->theChannel, (long)pVoice);
  1205.                     pVoice->voiceActive = TRUE;
  1206.                 }
  1207.             }
  1208.         }
  1209.     }
  1210.     return noErr;
  1211. }
  1212.  
  1213.  
  1214.  
  1215. Boolean HY_IsVoiceEmpty(register short int voiceNumber)
  1216. {
  1217.     SCStatus                status;
  1218.     register OSErr            theErr;
  1219.     register MM_SoundVoice    *pVoice;
  1220.     register Boolean        empty;
  1221.  
  1222.     empty = TRUE;
  1223.     if (voiceNumber == kUseAnyVoice)
  1224.     {
  1225.         voiceNumber = HYP_GetFreeVoice();
  1226.     }
  1227.  
  1228.     pVoice = HYP_GetPrivateDataFromVoice(voiceNumber);
  1229.     if (pVoice)
  1230.     {
  1231. // Our voiceActive works because we set it at the end of a sample playback. But we're going to query
  1232. // the Sound Manager, just in case it fails to call our callback that sets the flag and we'll store the 
  1233. // query in our variable voiceActive;
  1234.         empty = ! pVoice->voiceActive;
  1235.         theErr = SndChannelStatus(pVoice->theChannel, sizeof(SCStatus), &status);
  1236.         if (theErr == noErr)
  1237.         {
  1238.             if (pVoice->voiceActive != status.scChannelBusy)
  1239.             {
  1240.                 pVoice->voiceActive = status.scChannelBusy;
  1241.             }
  1242.             empty = ! pVoice->voiceActive;
  1243.         }
  1244.     }
  1245.     return empty;
  1246. }
  1247.  
  1248. void HY_SetRate(short int voiceNumber, UnsignedFixed newRate)
  1249. {
  1250.     register MM_SoundVoice    *pVoice;
  1251.     SndCommand            theCmd;
  1252.  
  1253.     if (voiceNumber == kUseAnyVoice)
  1254.     {
  1255.         voiceNumber = HYP_GetFreeVoice();
  1256.     }
  1257.     pVoice = HYP_GetPrivateDataFromVoice(voiceNumber);
  1258.     if (pVoice)
  1259.     {
  1260.         pVoice->lastRate = newRate;
  1261.         // Convert the passed rate into a relative sample multipler
  1262.         newRate = UnsignedFixedMulDiv(newRate, 0x10000, rate22khz);
  1263.  
  1264.         theCmd.param1 = 0;
  1265.         theCmd.param2 = (long)newRate;
  1266.         theCmd.cmd = rateCmd;
  1267.         SndDoImmediate(pVoice->theChannel, &theCmd);        // change rate now for this channel
  1268.     }
  1269. }
  1270.  
  1271. UnsignedFixed HY_GetRate(short int voiceNumber)
  1272. {
  1273.     register MM_SoundVoice    *pVoice;
  1274.     UnsignedFixed            oldRate;
  1275.     SndCommand            theCmd;
  1276.  
  1277.     if (voiceNumber == kUseAnyVoice)
  1278.     {
  1279.         voiceNumber = HYP_GetFreeVoice();
  1280.     }
  1281.     oldRate = rate22khz;
  1282.     pVoice = HYP_GetPrivateDataFromVoice(voiceNumber);
  1283.     if (pVoice)
  1284.     {
  1285.         oldRate = pVoice->lastRate;
  1286.         if (pVoice->voiceActive)
  1287.         {
  1288.             theCmd.param1 = 0;
  1289.             theCmd.param2 = (long)&oldRate;
  1290.             theCmd.cmd = getRateCmd;
  1291.             SndDoImmediate(pVoice->theChannel, &theCmd);        // get the current rate for this channel
  1292.  
  1293.             // Use this cool Toolbox routine to determine the actual sample rate
  1294.             oldRate = UnsignedFixedMulDiv(rate22khz, oldRate, 0x10000);
  1295.         }
  1296.     }
  1297.     return oldRate;
  1298. }
  1299.  
  1300. void HY_SetVolume(short int voiceNumber, SoundVolume newVolume)
  1301. {
  1302.     register MM_SoundVoice    *pVoice;
  1303.     SndCommand            theCmd;
  1304.  
  1305.     if (voiceNumber == kUseAnyVoice)
  1306.     {
  1307.         voiceNumber = HYP_GetFreeVoice();
  1308.     }
  1309.     pVoice = HYP_GetPrivateDataFromVoice(voiceNumber);
  1310.     if (pVoice)
  1311.     {
  1312.         theCmd.param1 = 0;
  1313.         // by breaking down the volumes here, we become a little more flexible to compilers
  1314.         theCmd.param2 = (long)((long)newVolume.right << 16L | (newVolume.left & 0xFFFF));
  1315.         theCmd.cmd = volumeCmd;
  1316.         SndDoImmediate(pVoice->theChannel, &theCmd);        // change volume now for this channel
  1317.     }
  1318. }
  1319.  
  1320. SoundVolume HY_GetVolume(short int voiceNumber)
  1321. {
  1322.     register MM_SoundVoice    *pVoice;
  1323.     SndCommand            theCmd;
  1324.     SoundVolume            oldVolume;
  1325.     long                    soundVolume;
  1326.  
  1327.     if (voiceNumber == kUseAnyVoice)
  1328.     {
  1329.         voiceNumber = HYP_GetFreeVoice();
  1330.     }
  1331.     *((long *)&oldVolume) = -1L;
  1332.     pVoice = HYP_GetPrivateDataFromVoice(voiceNumber);
  1333.     if (pVoice)
  1334.     {
  1335.         soundVolume = 0;
  1336.         theCmd.param1 = 0;
  1337.         theCmd.param2 = (long)&soundVolume;
  1338.  
  1339.         theCmd.cmd = getVolumeCmd;
  1340.         SndDoImmediate(pVoice->theChannel, &theCmd);        // get the current volume for this channel
  1341.  
  1342.         // by breaking down the volumes here, we become a little more flexible to compilers
  1343.         oldVolume.right = soundVolume >> 16L;
  1344.         oldVolume.left = soundVolume & 0xFFFFL;
  1345.     }
  1346.     return oldVolume;
  1347. }
  1348.  
  1349. void HY_SetStereoPosition(short int voiceNumber, short positionMask)
  1350. {
  1351.     register MM_SoundVoice    *pVoice;
  1352.     SoundVolume            theVolume;
  1353.  
  1354.     if (voiceNumber == kUseAnyVoice)
  1355.     {
  1356.         voiceNumber = HYP_GetFreeVoice();
  1357.     }
  1358.  
  1359.     pVoice = HYP_GetPrivateDataFromVoice(voiceNumber);
  1360.     if (pVoice)
  1361.     {
  1362.         theVolume.right = kFullVolume + positionMask;
  1363.         theVolume.left = kFullVolume - positionMask;
  1364.  
  1365.         HY_SetVolume(voiceNumber, theVolume);
  1366.     }
  1367. }
  1368.  
  1369. short HY_GetStereoPosition(short int voiceNumber)
  1370. {
  1371.     register MM_SoundVoice    *pVoice;
  1372.     SoundVolume            theVolume;
  1373.     short                positionMask;
  1374.  
  1375.     if (voiceNumber == kUseAnyVoice)
  1376.     {
  1377.         voiceNumber = HYP_GetFreeVoice();
  1378.     }
  1379.  
  1380.     pVoice = HYP_GetPrivateDataFromVoice(voiceNumber);
  1381.     if (pVoice)
  1382.     {
  1383.         theVolume = HY_GetVolume(voiceNumber);
  1384.         positionMask = theVolume.right - theVolume.left;
  1385.     }
  1386.     return positionMask;
  1387. }
  1388.  
  1389. void HY_ServiceTasks(void)
  1390. {
  1391.     register short int        count;
  1392.     SCStatus                status;
  1393.     register OSErr            theErr;
  1394.     register MM_SoundVoice    *pVoice;
  1395.  
  1396.     HYP_ProcessNextCallbackQueue();
  1397.  
  1398.     for (count = 0; count < maxSoundVoices; count++)
  1399.     {
  1400.         pVoice = &theSoundVoices[count];
  1401.         if (pVoice->theChannel)
  1402.         {
  1403.             theErr = SndChannelStatus(pVoice->theChannel, sizeof(SCStatus), &status);
  1404.             if (theErr == noErr)
  1405.             {
  1406.                 if (pVoice->voiceActive != status.scChannelBusy)
  1407.                 {
  1408.                     pVoice->voiceActive = status.scChannelBusy;
  1409.                 }
  1410.             }
  1411.         }
  1412.     }
  1413. }
  1414.  
  1415. OSErr HY_PauseHardware(void)
  1416. {
  1417.     register MM_SoundVoice    *pVoice;
  1418.     register short int        count;
  1419.  
  1420.     HYP_CleanupVBLTask();
  1421.  
  1422.     for (count = 0; count < maxSoundVoices; count++)
  1423.     {
  1424.         pVoice = &theSoundVoices[count];
  1425.         if (pVoice->theChannel)
  1426.         {
  1427.             if (pVoice->voicePaused == FALSE)
  1428.             {
  1429.                 pVoice->voicePaused = TRUE;
  1430.                 if (pVoice->voiceActive)
  1431.                 {
  1432.                     if (pVoice->filePlay)
  1433.                     {
  1434.                         SndPauseFilePlay(pVoice->theChannel);    // pause file playback
  1435.                     }
  1436.                 }
  1437.             }
  1438.         }
  1439.     }
  1440.     return noErr;
  1441. }
  1442.  
  1443. OSErr HY_ResumeHardware(void)
  1444. {
  1445.     register MM_SoundVoice    *pVoice;
  1446.     register short int        count;
  1447.  
  1448.     HYP_SetupVBLTask();
  1449.  
  1450.     for (count = 0; count < maxSoundVoices; count++)
  1451.     {
  1452.         pVoice = &theSoundVoices[count];
  1453.         if (pVoice->theChannel)
  1454.         {
  1455.             if (pVoice->voicePaused)
  1456.             {
  1457.                 pVoice->voicePaused = FALSE;
  1458.                 if (pVoice->voiceActive)
  1459.                 {
  1460.                     if (pVoice->filePlay)
  1461.                     {
  1462.                         SndPauseFilePlay(pVoice->theChannel);    // resume file playback
  1463.                     }
  1464.                 }
  1465.             }
  1466.         }
  1467.     }
  1468.     return noErr;
  1469. }
  1470.  
  1471. OSErr HY_StartFilePlay(short int voiceNumber, 
  1472.                         FSSpec *pFile, 
  1473.                         CustomCallbackProc customCallback,    
  1474.                         long userData,
  1475.                         long bufferSize,
  1476.                         Boolean killSound)
  1477. {
  1478.     register MM_SoundVoice    *pVoice;
  1479.     register OSErr            theErr;
  1480.  
  1481.     theErr = noErr;
  1482.     if (voiceNumber == kUseAnyVoice)
  1483.     {
  1484.         voiceNumber = HYP_GetFreeVoice();
  1485.         if (voiceNumber == kUseAnyVoice)
  1486.         {
  1487.             theErr = channelBusy;
  1488.         }
  1489.     }
  1490.     if (theErr == noErr)
  1491.     {
  1492.         pVoice = HYP_GetPrivateDataFromVoice(voiceNumber);        // voice to play on is in range
  1493.         if (pVoice)
  1494.         {
  1495.             if (pVoice->voiceActive)
  1496.             {
  1497.                 if (killSound)
  1498.                 {
  1499.                     HY_StopSample(voiceNumber);
  1500.                 }
  1501.                 else
  1502.                 {
  1503.                     theErr = channelBusy;
  1504.                 }
  1505.             }
  1506.             if (theErr == noErr)
  1507.             {
  1508.                 pVoice->customCallback = customCallback;
  1509.                 pVoice->userData = userData;
  1510.                 pVoice->theChannel->userInfo = globalA5;
  1511.                 pVoice->fileSpec = *pFile;
  1512.  
  1513. //    We could use the cooler file open function, but it only works with System 7 or better. Sound Manager 3.0
  1514. //    can be installed on a System eariler than 7.0
  1515. //                theErr = FSpOpenDF(pFile, fsRdPerm, &pVoice->fileRef);
  1516.  
  1517.                 theErr = HOpen(pFile->vRefNum, pFile->parID, pFile->name, fsRdPerm, &pVoice->fileRef);
  1518.                 if (theErr == noErr)
  1519.                 {
  1520.                     theErr = SndStartFilePlay(pVoice->theChannel, 
  1521.                                         pVoice->fileRef, 0, 
  1522.                                         bufferSize, NULL, 
  1523.                                         NULL, 
  1524.                                         NewFilePlayCompletionProc(HYP_FilePlayCompletionDone),
  1525.                                         TRUE);
  1526.                     if (theErr == noErr)
  1527.                     {
  1528.                         pVoice->filePlay = TRUE;
  1529.                         pVoice->voiceActive = TRUE;
  1530.                     }
  1531.                 }
  1532.             }
  1533.         }
  1534.     }
  1535.     return theErr;
  1536. }
  1537.  
  1538.  
  1539. OSErr HY_GetSoundResourceInformation(Handle theSnd, long *pLoopStart, long *pLoopEnd, 
  1540.                                     long *pSampleOffsetStart, long *pTotalSize, short *pBaseKey,
  1541.                                     short int *pNumChannels, short int *pBitSize,
  1542.                                     UnsignedFixed *pRate,
  1543.                                     short int *pCompressionType)
  1544. {
  1545.     register SoundHeader    *     pSndBuffer;
  1546.     register CmpSoundHeader *    pCmpBuffer;
  1547.     register ExtSoundHeader    *    pExtBuffer;
  1548.     short int                    soundFormat;
  1549.     short int                    numSynths, numCmds;
  1550.     long                        offset;
  1551.     register Ptr                pSndFormat;
  1552.     OSErr                    theErr;
  1553.  
  1554.     theErr = badFormat;
  1555.     *pSampleOffsetStart = 0;
  1556.     *pTotalSize = 0;
  1557.     *pLoopStart = 0;
  1558.     *pLoopEnd = 0;
  1559.     *pBaseKey = 0;
  1560.     *pCompressionType = notCompressed;
  1561.     *pNumChannels = 1;        // defaults for standard header
  1562.     *pBitSize = 8;
  1563.     *pRate = 0;
  1564.  
  1565.     if (theSnd)
  1566.     {
  1567.         HLock(theSnd);
  1568.         pSndFormat = (Ptr)*theSnd;
  1569.         soundFormat = *(short int *)pSndFormat;
  1570.         switch (soundFormat)
  1571.         {
  1572.             case 1:    // format 1 sound
  1573.                 // look inside the format 1 resource and decode offsets
  1574.                 numSynths = ((short int *)pSndFormat)[1];                    // get number of synths
  1575.                 numCmds = *(short int *)(pSndFormat + 4 + numSynths * 6);        // get number of commands
  1576.                 break;
  1577.             case 2:    // format 2 sound
  1578.                 numSynths = 0;        // format 2 has none
  1579.                 numCmds = ((short int *)pSndFormat)[2];
  1580.                 break;
  1581.             default:
  1582.                 soundFormat = -1;
  1583.                 break;
  1584.         }
  1585.  
  1586.         if (soundFormat != -1)    /* did we get the right format? */
  1587.         {
  1588.             /* compute address of sound header. 
  1589.             */
  1590.             offset = 6 + 6 * numSynths + 8 * numCmds;
  1591.             pSndBuffer = (SoundHeader *) (StripAddress(*theSnd) + offset);
  1592.             switch (pSndBuffer->encode)
  1593.             {
  1594.                 case stdSH:    // standard header
  1595.                     *pSampleOffsetStart = (long)&pSndBuffer->sampleArea[0] - (long)((Byte *)*theSnd);
  1596.                     *pTotalSize = pSndBuffer->length;
  1597.                     *pLoopStart = pSndBuffer->loopStart;
  1598.                     *pLoopEnd = pSndBuffer->loopEnd;
  1599.                     *pBaseKey = pSndBuffer->baseFrequency;
  1600.                     *pRate = pSndBuffer->sampleRate;
  1601.                     theErr = noErr;
  1602.                     break;
  1603.  
  1604.                 case extSH:    // extened header
  1605.                     pExtBuffer = (ExtSoundHeader *)pSndBuffer;
  1606.                     *pSampleOffsetStart = (long)&pExtBuffer->sampleArea[0] - (long)((Byte *)*theSnd);
  1607.                     *pNumChannels = pExtBuffer->numChannels;
  1608.                     *pBitSize = pExtBuffer->sampleSize;
  1609.                     *pTotalSize = pExtBuffer->numFrames * (*pNumChannels) * (*pBitSize / 8);
  1610.                     *pLoopStart = pExtBuffer->loopStart;
  1611.                     *pLoopEnd = pExtBuffer->loopEnd;
  1612.                     *pBaseKey = pExtBuffer->baseFrequency;
  1613.                     *pRate = pExtBuffer->sampleRate;
  1614.                     theErr = noErr;
  1615.                     break;
  1616.                     
  1617.                 case cmpSH:    // compressed header
  1618.                     pCmpBuffer = (CmpSoundHeader *)pSndBuffer;
  1619.                     *pSampleOffsetStart = (long)&pCmpBuffer->sampleArea[0] - (long)((Byte *)*theSnd);
  1620.                     *pNumChannels = pCmpBuffer->numChannels;
  1621.                     *pBitSize = pCmpBuffer->sampleSize;
  1622.                     *pTotalSize = pCmpBuffer->numFrames * (*pNumChannels) * (*pBitSize / 8);
  1623.                     *pLoopStart = pCmpBuffer->loopStart;
  1624.                     *pLoopEnd = pCmpBuffer->loopEnd;
  1625.                     *pBaseKey = pCmpBuffer->baseFrequency;
  1626.                     *pRate = pCmpBuffer->sampleRate;
  1627.                     *pCompressionType = pCmpBuffer->compressionID;
  1628.                     theErr = noErr;
  1629.                     break;
  1630.             }
  1631.         }
  1632.         HUnlock(theSnd);
  1633.     }
  1634.     return theErr;
  1635. }
  1636.  
  1637.  
  1638. OSErr HY_CreateAIFFFileFromPtr(FSSpec *pFile, Ptr pSample, long dataLength, UnsignedFixed sampleRate, 
  1639.                                         short bitSize, short numChannels)
  1640. {
  1641.     OSErr    theErr;
  1642.     short int    fileRef;
  1643.     long        length;
  1644.  
  1645.     theErr = badFormat;
  1646.     if ( (bitSize == 8) || (bitSize == 16) || (numChannels == 1) || (numChannels == 2) )
  1647.     {
  1648.         theErr = FSpCreate(pFile, 'hlly', AIFFID , 0);
  1649.         if (theErr == noErr)
  1650.         {
  1651.             theErr = FSpOpenDF(pFile, fsRdWrPerm, &fileRef);
  1652.             if (theErr == noErr)
  1653.             {
  1654.                 SetFPos(fileRef, fsFromStart, 0L);
  1655.                 theErr = SetupAIFFHeader(fileRef, numChannels, sampleRate, bitSize, NoneType, 
  1656.                                         0L, dataLength);
  1657.                 if (theErr == noErr)
  1658.                 {
  1659.                     length = dataLength * numChannels * (bitSize / 8);
  1660.                     theErr = FSWrite(fileRef, &length, pSample);
  1661.                     if (theErr == noErr)
  1662.                     {
  1663.                         SetFPos(fileRef, fsFromStart, 0L);
  1664.                         theErr = SetupAIFFHeader(fileRef, numChannels, sampleRate, bitSize, NoneType, 
  1665.                                                 length, dataLength);
  1666.                     }
  1667.                     FSClose(fileRef);
  1668.                 }
  1669.             }
  1670.         }
  1671.     }
  1672.     return theErr;
  1673. }
  1674.  
  1675. Handle HY_CreateSndResourceFromPtr(Ptr pSample, long dataLength, UnsignedFixed sampleRate, 
  1676.                                         short bitSize, short numChannels,
  1677.                                         short int baseFreq)
  1678. {
  1679.     OSErr    theErr;
  1680.     Handle    theSoundHeader;
  1681.     Handle    theSound;
  1682.     short    headerLength;
  1683.  
  1684.     theSound = NULL;
  1685.     // first allocate enough of a handle to setup the header information
  1686.     theSoundHeader = NewHandleClear(200L);
  1687.     if (theSoundHeader)
  1688.     {
  1689.         theErr = SetupSndHeader((SndListHandle)theSoundHeader, numChannels, sampleRate, bitSize, 'NONE',
  1690.                             baseFreq, 0, &headerLength);
  1691.         if (theErr == noErr)
  1692.         {
  1693.             // ok, now we know how large header info is, so we can block move the sample data right after
  1694.             theSound = NewHandle(headerLength + dataLength);
  1695.             if (theSound)
  1696.             {
  1697.                 HLock(theSound);
  1698.                 BlockMove(*theSoundHeader, *theSound, headerLength);
  1699.                 BlockMove(pSample, ((Byte *)*theSound) + headerLength, dataLength);
  1700.                 HUnlock(theSound);
  1701.                 DisposeHandle(theSoundHeader);
  1702.  
  1703.                 // ok, now set the new length of the data
  1704.                 theErr = SetupSndHeader((SndListHandle)theSound, numChannels, sampleRate, bitSize, 'NONE',
  1705.                                     baseFreq, dataLength, &headerLength);
  1706.                 if (theErr)
  1707.                 {
  1708.                     DisposeHandle(theSound);
  1709.                     theSound = NULL;
  1710.                 }
  1711.             }
  1712.         }
  1713.         else
  1714.         {
  1715.             DisposeHandle(theSoundHeader);
  1716.         }
  1717.     }
  1718.     return theSound;
  1719. }
  1720.  
  1721. Handle HY_CreateMACESndResourceFromPtr(short maceType, Ptr pSample, long dataLength, 
  1722.                                         UnsignedFixed sampleRate, 
  1723.                                         short bitSize, short numChannels,
  1724.                                         short int baseFreq)
  1725. {
  1726.     OSErr        theErr;
  1727.     Handle        theSoundHeader;
  1728.     Handle        theSound;
  1729.     short        headerLength;
  1730.     long            compressLength;
  1731.     OSType        compressionType;
  1732.     Byte            compressInState[128];
  1733.     Byte            compressOutState[128];
  1734.     Byte    *        pData;
  1735.     long            count;
  1736.  
  1737.     theErr = badFormat;
  1738.     theSound = NULL;
  1739.     if ( (bitSize == 8) && (numChannels == 1) )    // MACE only supports 8 bit samples. Hollywood only supports mono
  1740.     {
  1741.         switch (maceType)
  1742.         {
  1743.             case threeToOne:
  1744.                 compressionType = 'MAC3';
  1745.                 compressLength = dataLength / 3L;
  1746.                 theErr = noErr;
  1747.                 break;
  1748.             case sixToOne:
  1749.                 compressionType = 'MAC6';
  1750.                 compressLength = dataLength / 6L;
  1751.                 theErr = noErr;
  1752.                 break;
  1753.         }
  1754.     }
  1755.     if (theErr == noErr)
  1756.     {
  1757.         // first allocate enough of a handle to setup the header information
  1758.         theSoundHeader = NewHandleClear(200L);
  1759.         if (theSoundHeader)
  1760.         {
  1761.             theErr = SetupSndHeader((SndListHandle)theSoundHeader, 1, sampleRate, bitSize, compressionType,
  1762.                                 baseFreq, 0, &headerLength);
  1763.             if (theErr == noErr)
  1764.             {
  1765.                 // ok, now we know how large header info is, so we can block move the sample data right after
  1766.                 theSound = NewHandle(headerLength + dataLength);
  1767.                 if (theSound)
  1768.                 {
  1769.                     HLock(theSound);
  1770.                     BlockMove(*theSoundHeader, *theSound, headerLength);
  1771.  
  1772.                     // Clear state data for MACE compressor
  1773.                     for (count = 0; count < 128; count++)
  1774.                     {
  1775.                         compressInState[count] = 0;
  1776.                         compressOutState[count] = 0;
  1777.                     }
  1778.  
  1779.         // MACE supports stereo compressed data, but we don't bother to put it together here.
  1780.         // In order to create stereo compressed data, you need to build to buffers, one for each
  1781.         // channel. Compress each channel into that buffer by calling CompXto1 with the last parameter
  1782.         // set to the channel you are compressing. Then to rebuild the final data, you need to interleave
  1783.         // the two buffers into one buffer for the final output. Simple huh! :o
  1784.                     pData = ((Byte *)*theSound) + headerLength;
  1785.                     switch (maceType)
  1786.                     {
  1787.                         case threeToOne:
  1788.                             Comp3to1(pSample, pData, dataLength, 
  1789.                                             NULL, NULL, 1, 1);
  1790. //                                            compressInState, compressOutState, 1, 1);
  1791.                             break;
  1792.                         case sixToOne:
  1793.                             Comp6to1(pSample, pData, dataLength, 
  1794.                                             NULL, NULL, 1, 1);
  1795. //                                            compressInState, compressOutState, 1, 1);
  1796.                             break;
  1797.                     }
  1798.                     HUnlock(theSound);
  1799.                     SetHandleSize(theSound, headerLength + compressLength + 6);
  1800.                     theErr = MemError();
  1801.                     DisposeHandle(theSoundHeader);
  1802.                     if (theErr == noErr)
  1803.                     {
  1804.                         // ok, now set the new length of the data
  1805.                         theErr = SetupSndHeader((SndListHandle)theSound, 1, sampleRate, bitSize, compressionType,
  1806.                                             baseFreq, compressLength, &headerLength);
  1807.                     }
  1808.                     if (theErr)
  1809.                     {
  1810.                         DisposeHandle(theSound);
  1811.                         theSound = NULL;
  1812.                     }
  1813.                 }
  1814.             }
  1815.             else
  1816.             {
  1817.                 if (theSound)
  1818.                 {
  1819.                     DisposeHandle(theSoundHeader);
  1820.                 }
  1821.             }
  1822.         }
  1823.     }
  1824.     return theSound;
  1825. }
  1826.  
  1827.  
  1828.  
  1829. // EOF of Hollywood.c
  1830.  
  1831.